/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package lab5connectionpool;

import java.sql.SQLException;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author Daryna_Ragimova
 */
public class ConnectionPool {

    public static final int MAX_CONNECTIONS = 4;
    public static final int IDLE_TIMEOUT = 10000;
    private Semaphore semaphore = new Semaphore(MAX_CONNECTIONS, true);
    private Thread connectionThread;
    private Queue<Connection> connectionPool = new ConcurrentLinkedQueue<Connection>();
    private Set<Connection> workingConnections = new CopyOnWriteArraySet<Connection>();
    private ConnectionFactory factory = new ConnectionFactory();
    

    protected class ConnectionFactory {

        private int createdThreads = 0;

        public synchronized Connection getConnection() throws ConnectionException {
            if (createdThreads >= MAX_CONNECTIONS && connectionPool.size() == 0) {
                throw new ConnectionException("Connection limit exceeded.");
            }

            if (connectionPool.size() > 0) {
                return connectionPool.remove();
            }

            Connection c = new Connection();
            createdThreads++;
            if (connectionThread == null) {
                connectionThread = new ConnectionThread();
                connectionThread.start();
            }
            return c;
        }
    }

    protected class ConnectionThread extends Thread {

        @Override
        public void run() {
            while (!isInterrupted()) {
                for (Connection c : workingConnections) {
                    if (System.currentTimeMillis() - c.getLastUsed() > IDLE_TIMEOUT) {
                        checkin(c);
                    }
                }
            }
        }
    }

    public ConnectionPool(String url, String user, String passwd) throws SQLException {
        connectToDb(url, user, passwd);
    }

    public void interruptConnectionThread() {
        connectionThread.interrupt();
    }

    private void connectToDb(String url, String user, String passwd) throws SQLException {
        // do smth
    }

    public Connection checkout()
            throws InterruptedException, ConnectionException {
        return checkout(5000);
    }

    public Connection checkout(int waitingTimeout)
            throws InterruptedException, ConnectionException {
        if (semaphore.tryAcquire(waitingTimeout, TimeUnit.MILLISECONDS)) {
            Connection c = factory.getConnection();
            workingConnections.add(c);
            System.out.println(c.getName() + " checkouted");
            return c;
        } else {
            throw new ConnectionException("No free connections.");
        }
    }

    public void checkin(Connection c) {
        if (workingConnections.size() > 0){
            semaphore.release();
        }
        workingConnections.remove(c);
        connectionPool.add(c);
        System.out.println(c.getName() + " checkined");
    }
}
